var xpathEvaluator= new XPathEvaluator();
//var exprLJUser = xpathEvaluator.createExpression("//span[@class='ljuser']", null);
var exprLJUser = xpathEvaluator.createExpression("//img[contains(@src, '/img/userinfo.gif')]", null);
var exprAllDesc = xpathEvaluator.createExpression(".//descendant-or-self::*", null);
var exprPopup = xpathEvaluator.createExpression("ancestor-or-self::*[@id='popup']", null);
var exprPostsContainer = xpathEvaluator.createExpression("//*[count(.//a[contains(@href, '?mode=reply')])>2]", null);
var exprCountAncestors = xpathEvaluator.createExpression("count(ancestor::*)", null);
var exprReplyLink = xpathEvaluator.createExpression(".//a[contains(@href, '?mode=reply')]/@href", null);
var exprFoaf = xpathEvaluator.createExpression("//link[@rel='meta' and @title='FOAF']", null);
var converter =
  Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
    createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
var prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);

var useContext = true;

// we use UTF-8 here, you can choose other encodings.
converter.charset = "UTF-8";
var md5Obj = Components.classes["@mozilla.org/security/hash;1"]
                   .createInstance(Components.interfaces.nsICryptoHash);
md5Obj.init(md5Obj.MD5);
var helpers = {};
window.helpers = helpers;
var loadBannedStat;
var saveBannedStat;


var mDBConn = connectDb();
if(!mDBConn)
{
    dump("Cannot connect ljr-helper db!");
}

loadBannedStat = mDBConn.createStatement("SELECT banned_user FROM banned_users WHERE service = ?1 AND username = ?2");
saveBannedStat = mDBConn.createStatement("REPLACE INTO banned_users(service, username, banned_user) VALUES(?1, ?2, ?3)");
removeBannedStat = mDBConn.createStatement("DELETE FROM banned_users WHERE service=?1 AND username=?2 AND banned_user=?3");

function md5(str)
{
	// result is an out parameter,
	// result.value will contain the array length
	var result = {};
	// data is an array of bytes
	var data = converter.convertToByteArray(str, result);
	md5Obj.update(data, data.length);
	return md5Obj.finish(true);
}
function getPage(e)
{
	return e.target.tagObject.page;
	//return helpers[e.target.ownerDocument.location.host].pages[e.target.ownerDocument.location];
}
function init_LJR_Helper(doc)
{
	var loc = doc.location;
	if(loc)
	{
		var heads = doc.getElementsByTagName('head');
		if(heads.length && exprLJUser.evaluate(doc, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null)
		{
			if(!helpers[loc.host])
			{
				helpers[loc.host] = new LJR_Helper(doc);
			}
			var helper = helpers[loc.host];
			helper.pages[loc] = new LJR_Page(helper, doc);
		}

	}
}
function doc(e)
{
	return e.target.ownerDocument;
}
function getMouseX(e)
{
	return e.clientX + doc(e).body.scrollLeft;
}
function getMouseY(e)
{
	return e.clientY + doc(e).body.scrollTop;
}
// Based on: http://www.quirksmode.org/js/findpos.html
var getCumulativeOffset = function (obj) {
    var left, top;
    left = top = 0;
    if (obj.offsetParent) {
        do {
            left += obj.offsetLeft;
            top  += obj.offsetTop;
        } while (obj = obj.offsetParent);
    }
    return {
        x : left,
        y : top
    };
};

function fadeOut(ljrUserTag)
{
	var fadeInt = ljrUserTag.fadeIntervalId;
	if(ljrUserTag.fade == 0 &&  fadeInt != null)
	{
		if(!fadeInt)
			dump("fadeInt is null!");
		else
			clearInterval(fadeInt);
		ljrUserTag.fadeIntervalId = null;
		return;
	}
	else if(ljrUserTag.fade == 1)
	{
		if((ljrUserTag.opacity += 10) >= 100 && fadeInt != null)
		{
			ljrUserTag.fade = 0;
			if(!fadeInt)
				dump("fadeInt is null!");
			else
				clearInterval(fadeInt);
			ljrUserTag.fadeIntervalId = null;
		}

	}
	else if(ljrUserTag.fade == 2)
	{
		if((ljrUserTag.opacity -= 10) <= 0 && fadeInt != null)
		{
			ljrUserTag.fade = 0;
			ljrUserTag.hidePopup();
		}
	}

	ljrUserTag.popup.style.opacity = ljrUserTag.opacity/100.0;
}
function addFriendInner(obj, caller, iframe)
{
	var idoc = iframe.contentWindow.document;
	var form = idoc.forms[0];
	if(form)
    {
        clearInterval(caller.intervalID);
		if(obj.isFriend)
        {
                var actDel = idoc.createElement('input');
                actDel.name = 'action:delete';
                actDel.value = 'Remove';
                actDel.type = 'hidden';
                form.appendChild(actDel);
                form.submit();
                //TODO: check if removed
                obj.removeFriend();
        }
		else
        {
			form.submit();
			//TODO: check if added
			obj.addFriend();
		}
		obj.refreshPopup();

	}
}
function removeFriendInner(e)
{

	e.target.forms[0].submit();
}
function addFriend(obj, doc)
{
	var iframe = doc.createElement('iframe');
	iframe.style.display = 'none';
	doc.body.appendChild(iframe);
	iframe.src = 'http://' + doc.location.host + "/friends/add.bml?user=" + obj.userName;
	this.intervalID = setInterval(addFriendInner, 500, obj, this, iframe);
	iframe.contentWindow.document.forms[0].submit();

}

function LJR_UserTag(helper, page, node)
{
	this.helper = helper;
	this.page = page;
	this.userName = node.parentNode.nextSibling.textContent;
	//this.span = node;
	this.img = node
	this.opacity = 0;
	//var imgs = node.getElementsByTagName("img");
	//this.img = imgs[0];
	
	this.hidePopup = function()
	{
		if(!this.fadeIntervalId)
			dump("fadeInt is null!");
		else 
			clearInterval(this.fadeIntervalId);
		this.fadeIntervalId = null;

		if(this.popup)
		{
			//this.span.removeChild(this.popup);
			this.img.parentNode.parentNode.removeChild(this.popup);
			this.popup.tagObject = null;
			this.popup = null;

		}

	}
	this.addFriend = function()
	{
		this.helper.myFriends[this.userName] = true;
		this.isFriend = true;

	}
	this.removeFriend = function()
	{
		this.helper.myFriends[this.userName] = undefined;
		this.isFriend = false;
	}
	
	this.startHidePopup = function(e)
	{
		var targetInPopup = exprPopup.evaluate(e.target, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
		var relatedTargetInPopup = e.relatedTarget != null && exprPopup.evaluate(e.relatedTarget, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
		if( targetInPopup && e.relatedTarget == this.img || e.target == this.img && relatedTargetInPopup || targetInPopup && relatedTargetInPopup)
		{
			return;
		}
		/*if(this.dawnIntervalId)
		{
			clearInterval(this.dawnIntervalId);
			this.dawnIntervalId = null;
		}*/
		this.fade = 2;
		if(!this.fadeIntervalId)
		{
			this.fadeIntervalId = setInterval(fadeOut, 102, this);
		}
		//this.span.removeChild(this.popup);
		//this.popup = null;
	}
	this.fetchFoaf = function()
	{
		this.foaf = this.helper.getUserFriends(this.userName);
		this.isFriend = Boolean(this.helper.myFriends[this.userName]);
		this.isFriendOf = Boolean(this.foaf[this.helper.myUserName]);
	}
	this.getPopupHTML = function()
	{
		return '<div class="Inner"><div class="Content">' + 
			'<div class="Relation FriendStatus">' + this.userName + (this.isFriend? " is your" +(this.isFriendOf?" mutual":"")+ " friend":this.isFriendOf?" lists you as a friend":"") +'</div>' +
			'<div>' +
			'</div>' +
			/*'<span>' +
			  '	<a href="http://' + this.helper.host + '/inbox/compose.bml?user=' + this.userName + '">Send message</a>' +
			  '</span>' +*/
			//'<br>' +
			'<span>' +
			'	<span class="' + (this.isFriend?"RemoveFriend":"AddFriend") + '">' +
			'		<a href="javascript:void(0)" name="add_friend">' + (this.isFriend?"Remove":"Add") + ' friend</a>' +
			'	</span>' +
			'</span>' +
			'<br>' +
			/*'<span>' +
			  '	<a href="http://' + this.helper.host + '/shop/vgift.bml?to=' + this.userName + '">Send a virtual gift</a>' +
			  '</span>' +
			  '<br>' +*/
			/*'<span>' +
			'	<span class="SetBan">' +
			'		<a href="http://' + this.helper.host + '/manage/banusers.bml">Ban user</a>' +
			'	</span>' +
			'</span>' +
			'<br>' +*/
			'<span>' +
			'	<span class="SetBan">' +
			'		<a name="setban" href="javascript:void(0)">' +(this.helper.banned[this.userName]?'Unban': 'Ban') + ' user from ljr_fif</a>' +
			'	</span>' +
			'</span>' +
			'<br>' +
			'<span>View: </span>' +
			'<a href="http://' + this.helper.host + '/users/' + this.userName + '">Journal</a>' +
			'<span>&nbsp;| </span>' +
			'<a href="http://' + this.helper.host + '/userinfo.bml?user=' + this.userName + '">Profile</a>' +
			'<div class="ljclear">&nbsp;</div></div></div>';	
	}
	this.refreshPopup = function()
	{
		if(this.popup)
        {
            this.popup.innerHTML = this.getPopupHTML();
            this.popup.tagObject = this;
            var res = exprAllDesc.evaluate(this.popup, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
            if(res)
            {
                var item;
                var popupElements = [];
                while (item = res.iterateNext())
                {
                    popupElements.push(item);
                }
                for(var i =0; i < popupElements.length; i++)
                {
                    item = popupElements[i];
                    item.addEventListener('mouseout', function(e){ 
                            var popup = exprPopup.evaluate(e.target, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
                            if(popup != null)
                            {
                            popup.tagObject.startHidePopup(e); 
                            }
                            }, false);//'this.popup.showPopup(this)'; 
                    item.addEventListener('mouseover', function(e){ 
                            var popup = exprPopup.evaluate(e.target, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
                            if(popup != null)
                            {
                            popup.tagObject.showPopup(e); 
                            }
                            }, false);
                    if(item.name == 'setban')
                    {
                        item.helper = this.helper;
                        item.userName = this.userName;
                        item.tagObject = this;
                        item.addEventListener('click', BanFromLJRFif, false);
                        //item.onclick = "BanFromLJRFif(this,  this.helper.host, this.userName)";
                    }
                    if(item.name == 'add_friend')
                    {
                        //item.userName = this.userName;
                        //item.isFriend = this.isFriend;
                        item.tagObject = this;
                        item.addEventListener('click', function(e){ 
                                //addFriend(e.target.userName, e.target.ownerDocument, e.target.isFriend);
                                addFriend(e.target.tagObject, e.target.ownerDocument);
                                }, false);
                    }
                    //item.id = "popup-" + md5(item.innerHTML);
                    //item.className = 'in-popup';
                }
            }
        }
	}
	this.showPopup = function(e)
	{
		/*var targetInPopup = exprPopup.evaluate(e.target, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
		var relatedTargetInPopup = exprPopup.evaluate(e.relatedTarget, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue != null;
		if( targetInPopup && e.relatedTarget == this.img || e.target == this.img && relatedTargetInPopup || targetInPopup && relatedTargetInPopup)
		{
			return;
		}*/
		if(this.popup)
		{
			/*if(this.fadeIntervalId)
			{
				clearInterval(this.fadeIntervalId);
				this.fadeIntervalId = null;
			}*/
			if(!this.dawn && this.opacity < 100)
			{
				this.fade = 1;
			}
			return;

		}
		var mouseX = getMouseX(e);
		var mouseY = getMouseY(e);
		var popup = doc(e).createElement('div');
		popup.id = 'popup';
		popup.className = "ContextualPopup";
		popup.style.backgroundColor = '#fff';
		popup.innerHTML = "Loading...";
		popup.style.position = 'absolute'; //'position: absolute; padding: 10px; background: #ffffff; border: 1px solid black; opacity: 0.1; filter:alpha(opacity=100); ';
		this.popup = popup;
		//this.span.appendChild(popup);
		this.img.parentNode.parentNode.appendChild(popup);
		this.opacity = 0;
		popup.style.opacity = 0;
		this.fetchFoaf();
		this.refreshPopup();
		if(popup && popup.offsetLeft < mouseX && popup.offsetTop < mouseY 
				&& popup.offsetLeft + popup.offsetWidth >  mouseX && popup.offsetTop  + popup.offsetHeight > mouseY)
		{
			dump("Can't show popup! popup.offsetLeft=" + popup.offsetLeft + ", popup.offsetWidth=" + popup.offsetWidth + ", popup.offsetTop=" + popup.offsetTop + ", popup.offsetHeight =" + popup.offsetHeight);
			return;
		}
		var offset = getCumulativeOffset(this.img);
		if(doc(e).body.clientWidth  + doc(e).body.scrollLeft < offset.x - 10 + popup.offsetWidth)
		{
			popup.style.left = doc(e).body.clientWidth + doc(e).body.scrollLeft - popup.offsetWidth - 13;
		}
		else
		{
			popup.x = offset.x - 10;
		}
		if(doc(e).body.clientHeight  + doc(e).body.scrollTop < offset.y +  this.img.offsetHeight- 10 + popup.offsetHeight)
		{
			popup.style.top = doc(e).body.clientHeight  + doc(e).body.scrollTop -  popup.offsetHeight - 10;
		}
		else
		{
			popup.y = offset.y +  this.img.offsetHeight - 10;
		}

		this.fade = 1;
		this.fadeIntervalId = setInterval(fadeOut, 102, this);
		//popup.addEventListener('mouseout', function(e){ e.target.tagObject.startHidePopup(e); }, false);//'this.popup.showPopup(this)'; 
		//popup.addEventListener('mouseover', function(e){ e.target.tagObject.showPopup(e); }, false);//'this.popup.showPopup(this)'; 

	}

		//if(imgs && imgs.length)
	{
		//this.img = imgs[0];
		this.img.tagObject = this;
		if(!prefManager.getBoolPref("extensions.ljr-helper.useContextMenu"))
		{
			this.img.addEventListener('mouseover', function(e){ e.target.tagObject.showPopup(e); }, false);//'this.popup.showPopup(this)'; 
			this.img.addEventListener('mouseout', function(e){ e.target.tagObject.startHidePopup(e); }, false);//'this.popup.showPopup(this)'; 
		}
	}

}
function LJRUser()
{
}
function getCmdTarget(e)
{
	var node = e.target;
	if(node.parentNode == getContextMenu())
	{
		node = gContextMenu.target;
	}
	return node;
}
function BanFromLJRFif(e)
{
	var node = getCmdTarget(e);
	var tagObject = node.tagObject;
	var helper = tagObject.helper;//helpers[node.ownerDocument.location.host];
	var user = tagObject.userName;

	if(helper.banned[user])
		helper.removeBanned(user);
	else
		helper.saveBanned(user);
	if(tagObject.fadeInt)
		tagObject.hidePopup();
	tagObject.page.banAll(node.ownerDocument);

}
function getContextMenu()
{
		var menu = document.getElementById("contentAreaContextMenu");
		if(!menu)
		{
			menu =	window.opener.document.getElementById("contentAreaContextMenu");
		}
		return menu;
}
function LJR_Page(helper, doc)
{
	this.location = doc.location;
    this.helper = helper;
	if(prefManager.getBoolPref("extensions.ljr-helper.useContextMenu"))
	{
		var menu = getContextMenu();
		menu.addEventListener("popupshowing", contextPopupShowing, true);
		menu.addEventListener("popuphiding", contextPopupHiding, true);
	}
	else
	{
		var css = doc.createElement("link");
		css.rel = "stylesheet";
		css.type = "text/css";
		css.href = "http://p-stat.livejournal.com/contextualhover.css";
		var heads = doc.getElementsByTagName('head');
		heads[0].appendChild(css);

	}
	var res = exprLJUser.evaluate(doc, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
	var item;
	this.reUser = new RegExp('/users/\([^/]*\)/', 'i');
	while (item = res.iterateNext())
	{
		new LJR_UserTag(helper, this, item);
	}
	this.banAll = function(page)
	{
		this.helper.loadBanned();
		var res = exprPostsContainer.evaluate(page,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
		var container = res.snapshotItem(res.snapshotLength - 1);
		if(!container)
		{
			return;
		}
		var max = 0;
		for(var k = 0; k < res.snapshotLength; k++)
		{
			var count = exprCountAncestors.evaluate(res.snapshotItem(k), XPathResult.NUMBER_TYPE, null);
			if(count.numberValue > max)
			{
				max = Number(count.numberValue);
			}
		}
		for(var k = 0; k < res.snapshotLength; k++)
		{

			var container = res.snapshotItem(k);
			var count = exprCountAncestors.evaluate(container, XPathResult.NUMBER_TYPE, null);
			if(count.numberValue == max)
			{

				for(var i = 0; i < container.childNodes.length; i++)
				{
					var child = container.childNodes[i];
					res = exprReplyLink.evaluate(child,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
					var link = res.snapshotItem(res.snapshotLength - 1);
					if(link)
					{
						var match = this.reUser.exec(link.value);
						if(match)
						{
							var user = match[1];
							if(this.helper.banned[user])
							{
								//container.removeChild(child);
								child.style.display = 'none';
								//i--;
							}
							else
							{
								child.style.display = '';
							}
						}
					}
				}
			}
		}
	}
	this.banAll(doc);


}
function LJR_Helper(doc)
{
	this.host = doc.location.host;
	this.friendOf = {};
	this.foafs = {};
	this.banned = {};
	this.pages = {};
	var i = 0;
	this.GetMyFriends = function()
	{
			var cookieManager = Components.classes["@mozilla.org/cookiemanager;1"]
			.getService(Components.interfaces.nsICookieManager);

		var iter = cookieManager.enumerator;
		while (iter.hasMoreElements())
		{
			var cookie = iter.getNext();
			if (cookie instanceof Components.interfaces.nsICookie){
				if (cookie.host == this.host)
				{
					//menu.appendItem(cookie.name,cookie.value);
					if(cookie.name == 'ljsession')
					{
						var parts = cookie.value.split(':');
						if(parts instanceof Array && parts.length > 1)
						{
							this.myUserName = parts[1];
							if(!this.myFriends)
							{
								this.myFriends = this.loadFOAF(this.myUserName);
							}
						}

					}
				}
			}
		}


	}
	this.loadFOAF = function(userName)
	{
		var req = new XMLHttpRequest();  
		req.open('GET', 'http://' + this.host + '/users/' + userName + '/data/foaf', false);   
			req.send(null);  
		if(req.status == 200)  
		{
			dump(req.responseText);  
			var xmldoc = req.responseXML;
			try
			{
				var nickNodes = xmldoc.evaluate('//foaf:knows/foaf:Person/foaf:nick', xmldoc, document.createNSResolver(xmldoc), XPathResult.ANY_TYPE, null);
				//var nickNodes = xmldoc.getElementsByTagName('foaf:nick');
				var res = {};

				var item;
				var i = 0;
				while (item = nickNodes.iterateNext())

				{
					res[item.textContent] = {'userName':item.textContent};
				}
				return res;
			}
			catch(ex)
			{
				dump(ex);
			}
		}
		else 
		{
			return null;
		}

	}
	this.getUserFriends = function(userName)
	{
		if(!this.foafs[userName])
		{
			this.foafs[userName] = this.loadFOAF(userName);
		
		}
		return this.foafs[userName];
	}
	this.addBanned = function(userName)
	{
		this.banned[userName] = true;
	}
	this.removeBanned = function(userName)
	{
		this.banned[userName] = undefined;
		removeBannedStat.bindUTF8StringParameter(0, this.host);
		removeBannedStat.bindUTF8StringParameter(1, this.myUserName);
		removeBannedStat.bindUTF8StringParameter(2, userName);
		removeBannedStat.execute();
		removeBannedStat.reset();
	}
	this.saveBanned = function(userName)
	{
		this.addBanned(userName);
		saveBannedStat.bindUTF8StringParameter(0, this.host);
		saveBannedStat.bindUTF8StringParameter(1, this.myUserName);
		saveBannedStat.bindUTF8StringParameter(2, userName);
		saveBannedStat.execute();
		saveBannedStat.reset();
	}
	this.loadBanned = function()
	{
		try
		{
			loadBannedStat.bindUTF8StringParameter(0, this.host);
			loadBannedStat.bindUTF8StringParameter(1, this.myUserName);
			while (loadBannedStat.executeStep()) 
			{
				this.addBanned(loadBannedStat.getString(0));
			}
			loadBannedStat.reset();
		}
		catch(ex)
		{
			dunp("Cannot load banned users list! Exception " + ex);
		}

	}
	this.loadBanned();


	var res = exprLJUser.evaluate(doc.body, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
	if(res)
	{
		try
		{
			this.GetMyFriends();
		}
		catch(ex)
		{
			dump(ex);
		}

	}

}

function contextPopupShowing(e)
{
	if (e.target != getContextMenu() || !gContextMenu.target.tagObject || !gContextMenu.onImage )
	{
		return true;
	}
	var node;
	var tag = gContextMenu.target.tagObject;
	tag.fetchFoaf();
	if(tag.isFriend)
	{
		node = document.getElementById("delfriend-ljr");
	}
	else
	{
		node = document.getElementById("addfriend-ljr");
	}
	node.addEventListener('command',function(e){ 
			var target = getCmdTarget(e);

			addFriend(target.tagObject, target.ownerDocument);
			}, false);
	node.hidden = false;
	//document.getElementById("banuser-ljr").hidden = false;
	if(tag.helper.banned[tag.userName])
	{
		node = document.getElementById("unbanfromfif-ljr");
	}
	else
	{
		node = document.getElementById("banfromfif-ljr");
	}
	node.hidden = false;
	node.addEventListener('command', BanFromLJRFif, false);
	return true;

}

function contextPopupHiding(e)
{
	document.getElementById("addfriend-ljr").hidden = true;
	document.getElementById("delfriend-ljr").hidden = true;
	document.getElementById("banuser-ljr").hidden = true;
	document.getElementById("unbanfromfif-ljr").hidden = true;
	document.getElementById("banfromfif-ljr").hidden = true;
	return true;
}
function enumAllDocuments(action)
{
	var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
		.getService(Components.interfaces.nsIWindowMediator);
	var browserEnumerator = wm.getEnumerator("navigator:browser");

	// Check each browser instance for our URL
	var found = false;
	while (!found && browserEnumerator.hasMoreElements()) 
	{
		var browserWin = browserEnumerator.getNext();
		if(browserWin.getBrowser)
		{
			var tabbrowser = browserWin.getBrowser();

			// Check each tab of this browser instance
			var numTabs = tabbrowser.browsers.length;
			for(var index=0; index<numTabs; index++) 
			{
				var currentBrowser = tabbrowser.getBrowserAtIndex(index);
				action(currentBrowser.contentDocument);
			}
		}
	}

}

document.addEventListener("load", function(e) { init_LJR_Helper(e.target); }, true); 
//document.addEventListener("unload", cleanup_LJR_Helper, true); 
